package com.derbysoft.dswitch.adapter.ihg.dswitch3.translate.impl;

import com.derbysoft.dswitch.adapter.ihg.common.utils.CollectionUtil;
import com.derbysoft.dswitch.adapter.ihg.common.utils.DateTimeUtils;
import com.derbysoft.dswitch.adapter.ihg.common.utils.DateUtils;
import com.derbysoft.dswitch.adapter.ihg.dto.Rate;
import com.derbysoft.dswitch.adapter.ihg.dto.RateChangeInfo;
import com.derbysoft.dswitch.adapter.ihg.dto.TimeSpan;
import com.derbysoft.dswitch.dto.common.KeyValue;
import com.derbysoft.dswitch.dto.common.TPAExtensionsDTO;
import com.derbysoft.dswitch.dto.hotel.common.DateRangeDTO;
import com.derbysoft.dswitch.dto.hotel.common.RateDTO;

import java.math.BigDecimal;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

public abstract class IhgRates2RateDTOsTranslator {

    private static final String MAX_OCC = "MaxOccupancy";

    public static List<RateDTO> translate(List<Rate> rates, TimeSpan requestDateRange) {
        Rate rate = rates.get(0);

        List<RateChangeInfo> changeInfo = rate.getTPAExtensions().getRateChangeInfo();

        return CollectionUtil.isEmpty(changeInfo)
                ? createNoChangedRates(rate, requestDateRange)
                : createChangedRates(rate, changeInfo, requestDateRange);
    }

    private static List<RateDTO> createChangedRates(Rate rate, List<RateChangeInfo> rateChanges, TimeSpan requestDateRange) {
        parseChangeInfo(rateChanges);

        List<RateDTO> rateDTOs = new ArrayList<RateDTO>();

        for (RateChangeInfo rateChange : rateChanges) {
            rateDTOs.add(createRateDTO(rateChange.getAmount(), rateChange.getStart(), DateUtils.prev(rateChange.getEnd())));
        }

        Date checkInDate = DateTimeUtils.parse(requestDateRange.getStart());
        int dayRangeBetween2Date = DateUtils.getDayRangeBetween2Date(requestDateRange.getStart(), requestDateRange.getEnd());
        for (int i = 0; i < dayRangeBetween2Date; i++) {
            Date date = DateTimeUtils.addDays(checkInDate, i);
            String dateStr = DateTimeUtils.formatDate(date);
            if (findRate(date, rateDTOs) == null) {
                if (dateStr.equals(requestDateRange.getStart())) {
                    rateDTOs.add(createRateDTO(rate, dateStr, dateStr));
                    continue;
                }
                RateDTO rateDTO = findRate(DateUtils.prev(date), rateDTOs);
                if (rateDTO == null || BigDecimal.valueOf(rateDTO.getAmountBeforeTax()).compareTo(rate.getBase().getAmountBeforeTax()) != 0) {
                    rateDTOs.add(createRateDTO(rate, dateStr, dateStr));
                } else {
                    if (DateUtils.next(rateDTO.getDateRange().getEnd()).equals(requestDateRange.getEnd())) {
                        rateDTO.getDateRange().setEnd(DateUtils.prev(dateStr));
                    } else {
                        rateDTO.getDateRange().setEnd(DateUtils.next(rateDTO.getDateRange().getEnd()));
                    }
                }
            }
        }
        sortRates(rateDTOs);

        return rateDTOs;
    }

    private static RateDTO findRate(Date date, List<RateDTO> rateDTOs) {
        for (RateDTO rateDTO : rateDTOs) {
            if (date.compareTo(DateTimeUtils.parse(rateDTO.getDateRange().getStart())) >= 0
                    && date.compareTo(DateTimeUtils.parse(rateDTO.getDateRange().getEnd())) <= 0) {
                return rateDTO;
            }
        }
        return null;
    }

    private static List<RateDTO> createNoChangedRates(Rate rate, TimeSpan requestDateRange) {
        List<RateDTO> rateDTOs = new ArrayList<RateDTO>();
        rateDTOs.add(createRateDTO(rate, requestDateRange.getStart(), DateUtils.prev(requestDateRange.getEnd())));
        return rateDTOs;
    }

    private static void parseChangeInfo(List<RateChangeInfo> changes) {
        for (RateChangeInfo changeInfo : changes) {
            processEffectivePeriod(changeInfo);
            processRateChange(changeInfo);
        }
    }

    private static void processEffectivePeriod(RateChangeInfo changeInfo) {
        String effectivePeriod = null;
        if (changeInfo.getEffectivePeriod() != null) {
            try {
                effectivePeriod = changeInfo.getEffectivePeriod().trim();
                changeInfo.setEffectivePeriod(effectivePeriod);
                effectivePeriod = effectivePeriod.replaceAll("EFFECTIVE: ", "");
                String[] split = effectivePeriod.split(",");
                changeInfo.setStart(DateTimeUtils.formatDate(getDateFormat().parse(split[0])));
                changeInfo.setEnd(DateTimeUtils.formatDate(getDateFormat().parse(split[1])));
            } catch (Exception e) {
                throw new IllegalArgumentException("The argument EffectivePeriod format is illegal, "
                        + "that should be 'EFFECTIVE: 2007-01-12,2007-26-12' ,but this is[" + effectivePeriod + "]", e);
            }
        }
    }

    public static DateFormat getDateFormat() {
        return new SimpleDateFormat("yyyy-dd-MM");
    }

    private static void processRateChange(RateChangeInfo changeInfo) {
        String rateChangeText = null;
        try {
            rateChangeText = changeInfo.getRateChange().trim();
            rateChangeText = rateChangeText.replaceAll("Rate Change: ", "");
            String[] split = rateChangeText.split(" ");
            changeInfo.setAmount(new BigDecimal(split[0]));
            changeInfo.setCurrencyCode(split[1]);
        } catch (Exception e) {
            throw new IllegalArgumentException("The argument RateChange format is illegal, "
                    + "that should be 'Rate Change: 1195.00 CNY' ,but this is[" + rateChangeText + "]", e);
        }
    }

    private static RateDTO createRateDTO(BigDecimal amount, String star, String end) {
        RateDTO rateDTO = new RateDTO();
        rateDTO.setDateRange(new DateRangeDTO(star, end));
        rateDTO.setAmountBeforeTax(amount.doubleValue());
        return rateDTO;
    }

    private static RateDTO createRateDTO(Rate rate, String start, String end) {
        RateDTO rateDTO = createRateDTO(rate.getBase().getAmountBeforeTax(), start, end);
        if (rate.getTPAExtensions() != null && rate.getTPAExtensions().getMaxOccupancy() != null) {
            TPAExtensionsDTO tpaExtensionsDTO = new TPAExtensionsDTO();
            ArrayList<KeyValue> elements = new ArrayList<KeyValue>();
            elements.add(new KeyValue(MAX_OCC, String.valueOf(rate.getTPAExtensions().getMaxOccupancy())));
            tpaExtensionsDTO.setElementsList(elements);
            rateDTO.setTpaExtensions(tpaExtensionsDTO);
        }
        return rateDTO;
    }

    private static void sortRates(List<RateDTO> rateDTOs) {
        Collections.sort(rateDTOs, new Comparator<RateDTO>() {
            public int compare(RateDTO o1, RateDTO o2) {
                return DateTimeUtils.parse(o1.getDateRange().getEnd()).compareTo(DateTimeUtils.parse(o2.getDateRange().getEnd()));
            }
        });
    }
}
